// Make Shapes (SIZE variant) // ... based on colour and that modifies the size // Samples N random pixel positions from the first raster/image layer on the current spread, // reads the RGB colour at each position, and places a filled shape of that colour centred - and factors the size based on the colour average // on that pixel's location. All generated shapes are grouped into a single layer on completion. // Each shape gets independently randomised size, rotation, opacity and effect. // For 'Square', width and height use the same random value to stay proportional. // Rotation is applied around each shape's own centre point. // // Requires at least one raster or image layer on the current spread. 'use strict'; const { Document } = require('/document.js'); const { PixelReaderRGBA8 } = require('/pixelaccessor.js'); const { Shape, ShapeType } = require('/shapes.js'); const { ShapeNodeDefinition, PolyCurveNodeDefinition, ContainerNodeDefinition, NodeChildType } = require('/nodes.js'); const { FillDescriptor } = require('/fills.js'); const { RGBA8 } = require('/colours.js'); const { CurveBuilder, PolyCurve, Transform } = require('/geometry.js'); const { LineStyleDescriptor } = require('/linestyle.js'); const { AddChildNodesCommandBuilder, DocumentCommand, CompoundCommandBuilder, NodeMoveType } = require('/commands.js'); const { BevelEmbossLayerEffect, PhongBevelLayerEffect, OuterShadowLayerEffect } = require('/layereffects.js'); const { Selection } = require('/selections.js'); const { Dialog, DialogResult } = require('/dialog.js'); const { UnitType } = require('/units.js'); const { app } = require('/application.js'); const doc = Document.current; if (!doc) { app.alert('No active document.'); throw new Error('No document');} const spread = doc.currentSpread; //if (!rasterNode) { throw new Error('No raster or image layer found.'); } let rasterNode = null; spread.layers.forEach(n => { if (!rasterNode && (n.isRasterNode || n.isImageNode)) rasterNode = n; }); if (!rasterNode) { app.alert('No raster or image layer found.\nPlease ensure a pixel or image layer exists on the spread.'); throw new Error('No raster'); } const shapeOptions = [ { label: 'Square', type: ShapeType.Rectangle, square: true }, { label: 'Rectangle', type: ShapeType.Rectangle, square: false }, { label: 'Circle / Ellipse', type: ShapeType.Ellipse, square: false }, { label: 'Triangle', type: ShapeType.Triangle, square: false }, { label: 'Diamond', type: ShapeType.Diamond, square: false }, { label: 'Pentagon', type: ShapeType.Polygon, square: false }, { label: 'Star', type: ShapeType.Star, square: false }, { label: 'Square Star', type: ShapeType.SquareStar, square: false }, { label: 'Heart', type: ShapeType.Heart, square: false }, { label: 'Crescent', type: ShapeType.Crescent, square: false }, { label: 'Cog', type: ShapeType.Cog, square: false }, { label: 'Cloud', type: ShapeType.Cloud, square: false }, { label: 'Arrow', type: ShapeType.Arrow, square: false }, { label: 'Trapezoid', type: ShapeType.Trapezoid, square: false }, ]; const patterns = ["none", "average", "negative", "red", "green", "blue", "average 1", "average 2","average 3","average 4","multiple","threshold"]; const dlg = Dialog.create('Make Shapes (size variant)'); const colL = dlg.addColumn(); //const colR = dlg.addColumn(); const g1 = colL.addGroup(''); const cmbShape = g1.addComboBox('Shape', shapeOptions.map(s => s.label), 0); const edtCount = g1.addUnitValueEditor('Number of shapes', UnitType.Number, UnitType.Number, 1000, 1, 100000); const edtMinWidth = g1.addUnitValueEditor( 'Min width (px)', UnitType.Pixel, UnitType.Pixel, 200, 1, 10000); const edtMaxWidth = g1.addUnitValueEditor( 'Max width (px)', UnitType.Pixel, UnitType.Pixel, 200, 1, 10000); const edtMinHeight = g1.addUnitValueEditor( 'Min height (px)', UnitType.Pixel, UnitType.Pixel, 200, 1, 10000); const edtMaxHeight = g1.addUnitValueEditor( 'Max height (px)', UnitType.Pixel, UnitType.Pixel, 200, 1, 10000); const edtRotMin = g1.addUnitValueEditor('Rotation min (\u00B0)', UnitType.Degree, UnitType.Degree, 0, -360, 360); const edtRotMax = g1.addUnitValueEditor('Rotation max (\u00B0)', UnitType.Degree, UnitType.Degree, 0, -360, 360); const minSize = g1.addUnitValueEditor("Min size factor (0.1 to 255)", UnitType.Number, UnitType.Number, 0.1, 0.1, 255).setPrecision(2); const maxSize = g1.addUnitValueEditor("Max size factor (0.1 to 255)", UnitType.Number, UnitType.Number, 255, 0.1, 255).setPrecision(2); const redF = g1.addUnitValueEditor("Red factor (0.1 to 10)", UnitType.Number, UnitType.Number, 1, 0.1, 10).setPrecision(2); const greenF = g1.addUnitValueEditor("Green factor (0.1 to 10)", UnitType.Number, UnitType.Number, 1, 0.1, 10).setPrecision(2); const blueF = g1.addUnitValueEditor("Blue factor (0.1 to 10)", UnitType.Number, UnitType.Number, 1, 0.1, 10).setPrecision(2); dlg.patterns = g1.addComboBox("Pattern", patterns, 0); const result = dlg.runModal(); if (result.value !== DialogResult.Ok.value) return; const selected = shapeOptions[cmbShape.selectedIndex]; const count = Math.round(edtCount.value); let minW = edtMinWidth.value, maxW = edtMaxWidth.value; if (minW > maxW) { const t = minW; minW = maxW; maxW = t; } let minH = edtMinHeight.value, maxH = edtMaxHeight.value; if (minH > maxH) { const t = minH; minH = maxH; maxH = t; } let rotMin = edtRotMin.value, rotMax = edtRotMax.value; if (rotMin > rotMax) { const t = rotMin; rotMin = rotMax; rotMax = t; } let sizMin = minSize.value, sizMax = maxSize.value; if (sizMin > sizMax) { const t = sizMin; sizMin = sizMax; sizMax = t; } const useRotation = rotMin !== 0 || rotMax !== 0; doc.executeCommand(DocumentCommand.createSetCurrentSpread(spread)); const rw = rasterNode.rasterWidth; const rh = rasterNode.rasterHeight; const bb = rasterNode.getSpreadBaseBox(false); const ri = rasterNode.rasterInterface; const bitmap = ri.createCompatibleBitmap(true); const reader = PixelReaderRGBA8.create(bitmap); const shape = Shape.create(selected.type); const lineStyle = null; const lineFill = null; const transFill = null; const placeBuilder = AddChildNodesCommandBuilder.create(); placeBuilder.setInsertionTarget(spread); for (let i = 0; i < count; i++) { const px = Math.floor(Math.random() * rw); const py = Math.floor(Math.random() * rh); const pixel = reader.readPixel(px, py); const fill = FillDescriptor.createSolid(RGBA8(pixel.r, pixel.g, pixel.b, pixel.alpha)); let red1 = pixel.r * redF.value; let green1 = pixel.g * greenF.value; let blue1 = pixel.b * blueF.value; let avg = (red1 + green1+ blue1)/3; // crude average ! switch (dlg.patterns.selectedIndex) { case 0: avg = 255; break; case 1: break; case 2: avg = 255 - avg; break; case 3: avg = red1; break; case 4: avg = green1; break; case 5: avg = blue1; break; case 6: avg = (red1*2 + green1+ blue1)/6; break; case 7 : avg = (red1+green1+blue1); break; case 8 : avg = 255 - (red1+green1+blue1); break; case 9: avg = red1+avg; break; case 10: avg = (red1 * green1 * blue1) / (255*255*255); break; case 11: if (avg>125){avg=255;}else{avg=0;}; break; } const pcx = bb.x + (px / rw) * bb.width; const pcy = bb.y + (py / rh) * bb.height; if (avg > sizMax) {avg = sizMax;} if (avg < sizMin) {avg = sizMin;} let shapeW = minW + Math.random() * (maxW - minW); let shapeH = minH + Math.random() * (maxH - minH); shapeW = avg * shapeW / 255; shapeH = avg * shapeH / 255; placeBuilder.addNode(ShapeNodeDefinition.create(shape, { x: pcx - shapeW / 2, y: pcy - shapeH / 2, width: shapeW, height: shapeH }, fill, null)); } const placeCmd = placeBuilder.createCommand(false, NodeChildType.Main); doc.executeCommand(placeCmd); const newNodes = []; placeCmd.newNodes.forEach(n => newNodes.push(n)); if (useRotation) { const compound = CompoundCommandBuilder.create(); newNodes.forEach(node => { const sel = Selection.create(doc, node); if (useRotation) { const rotRad = (rotMin + Math.random() * (rotMax - rotMin)) * Math.PI / 180; const nodeBb = node.getSpreadBaseBox(false); const originX = nodeBb.x + nodeBb.width / 2; const originY = nodeBb.y + nodeBb.height / 2; let combined = Transform.createTranslate(-originX, -originY); combined = Transform.createRotate(rotRad).multiply(combined); combined = Transform.createTranslate(originX, originY).multiply(combined); compound.addCommand(DocumentCommand.createTransform(sel, combined, { mergeable: false })); } }); doc.executeCommand(compound.createCommand()); } // Group all generated shapes into a single container doc.executeCommand(DocumentCommand.createSetCurrentSpread(spread)); const grpBuilder = AddChildNodesCommandBuilder.create(); grpBuilder.setInsertionTarget(spread); grpBuilder.addContainerNode(ContainerNodeDefinition.create('Make Shapes Group')); const grpCmd = grpBuilder.createCommand(false, NodeChildType.Main); doc.executeCommand(grpCmd); const grpNodes = []; grpCmd.newNodes.forEach(n => grpNodes.push(n)); const container = grpNodes[0]; // Move each node individually into the container const moveCompound = CompoundCommandBuilder.create(); newNodes.forEach(node => { moveCompound.addCommand( DocumentCommand.createMoveNodes(Selection.create(doc, node), container, NodeMoveType.Inside, NodeChildType.Main) ); }); doc.executeCommand(moveCompound.createCommand());